package br.com.afi.pattern.business;

import java.io.Serializable;
import java.util.List;

import br.com.afi.pattern.converter.Converter;
import br.com.afi.pattern.dao.GenericDAO;
import br.com.afi.pattern.model.Model;
import br.com.afi.pattern.vo.ValueObject;
import br.com.afi.pattern.wrapper.Wrapper;
import br.com.afi.pattern.wrapper.WrapperList;

/**
 * Essa classe abstrata possui as quatro operações básicas (Select, Insert, Update, Delete)
 * implementadas. Porém todos os métodos retornam e recebem um tipo Wrapper, onde o objeto pode
 * facilmente ser obtido como {@link ValueObject} ou {@link Model}. Para que isso seja possível é
 * necessário implementar o método {@link #createDefaultDAO()} e informar o {@link Converter} no
 * construtor.
 * 
 * @author André de Fontana Ignacio
 * @version 1.0
 * @param <E> Tipo do Model (Entity)
 * @param <ID> Tipo do Id do Model (Entity)
 * @param <V> Value Object que representa o Model (Entity).
 */
public abstract class AbstractBusinessWrapperImpl<E extends Model<ID>, ID extends Serializable, V extends ValueObject> implements BusinessWrapper<E, ID, V>
{
	private Converter<E, V> converter;

	/**
	 * Cria o DAO padrão para esta classe.
	 * 
	 * @return DAO
	 */
	public abstract GenericDAO<E, ID> createDefaultDAO();

	/**
	 * Construtor
	 * 
	 * @param converter Conversor
	 */
	public AbstractBusinessWrapperImpl( Converter<E, V> converter )
	{
		super();
		this.converter = converter;
	}
	
	/**
	 * Construtor
	 */
	public AbstractBusinessWrapperImpl()
	{
		super();
	}
	
	/**
	 * Atribui o conversor entre ValueObject e o Model (Entity)
	 * 
	 * @param converter Conversor
	 */
	public void setConverter(Converter<E, V> converter)
	{
		this.converter = converter;
	}

	public Wrapper<E, V> createWrapper( E m )
	{
		return new Wrapper<E, V>( m, converter );
	}

	public Wrapper<E, V> createWrapper( V v )
	{
		return new Wrapper<E, V>( v, converter );
	}

	public WrapperList<E, V> createWrapperListFromModel( List<E> ms )
	{
		return new WrapperList<E, V>( ms, converter, true );
	}

	public WrapperList<E, V> createWrapperListFromVO( List<V> vos )
	{
		return new WrapperList<E, V>( vos, converter );
	}

	@Override
	public void delete( Wrapper<E, V> w ) throws BusinessException
	{
		final GenericDAO<E, ID> dao = createDefaultDAO();
		dao.makeTransient( w.asModel() );
	}

	@Override
	public WrapperList<E, V> findAll()
	{
		final GenericDAO<E, ID> dao = createDefaultDAO();
		final List<E> models = dao.findAll();
		return createWrapperListFromModel( models );
	}

	@Override
	public WrapperList<E, V> findByExample( Wrapper<E, V> example, String... excludeProperty )
	{
		final GenericDAO<E, ID> dao = createDefaultDAO();
		final E model = example.asModel();
		final List<E> models = dao.findByExample( model, excludeProperty );
		return createWrapperListFromModel( models );
	}

	@Override
	public WrapperList<E, V> findByExampleILike( Wrapper<E, V> example, boolean includeRelationships, String... excludeProperty )
	{
		final GenericDAO<E, ID> dao = createDefaultDAO();
		final E model = example.asModel();
		final List<E> models = dao.findByExampleILike( model, includeRelationships, excludeProperty );
		return createWrapperListFromModel( models );
	}

	@Override
	public Wrapper<E, V> findById( ID id )
	{
		final GenericDAO<E, ID> dao = createDefaultDAO();
		return createWrapper( dao.findById( id, false ) );
	}

	@Override
	public boolean existsById( ID id )
	{
		final GenericDAO<E, ID> dao = createDefaultDAO();
		return dao.existsById( id );
	}

	@Override
	public Wrapper<E, V> save( Wrapper<E, V> w ) throws BusinessException
	{
		final GenericDAO<E, ID> dao = createDefaultDAO();
		final E model = dao.makePersistent( w.asModel() );
		return createWrapper( model );
	}

}
